package com.egor.views.components.modalboxes;

import com.egor.data.entity.Department;
import com.egor.data.entity.Employee;
import com.egor.services.DepartmentService;
import com.vaadin.flow.component.ComponentEvent;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.Key;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.button.ButtonVariant;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.datepicker.DatePicker;
import com.vaadin.flow.component.dialog.Dialog;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.binder.BeanValidationBinder;
import com.vaadin.flow.data.binder.Binder;
import com.vaadin.flow.data.binder.ValidationException;
import com.vaadin.flow.shared.Registration;
import com.vaadin.flow.spring.annotation.RouteScope;
import lombok.Getter;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.util.List;

@RouteScope
@Component
public class EditEmployeeModalBox extends Dialog {
    private Employee employee;
    private final List<Department> departmentList;
    private final VerticalLayout layout = new VerticalLayout();
    private final TextField firstName = new TextField("First name");
    private final TextField secondName = new TextField("Second name");
    private final TextField lastName = new TextField("Last name");
    private final DatePicker birthday = new DatePicker("Date of birth");
    private final ComboBox<Department> department = new ComboBox<>("Department");
    private final Binder<Employee> binder = new BeanValidationBinder<>(Employee.class);
    private final Button save = new Button("Save");
    private final Button cancel = new Button("Cancel");

    public EditEmployeeModalBox(DepartmentService departmentService) {
        this.departmentList = departmentService.listAll();

        setClassName("employee-edit");
        setHeaderTitle("Edit/create employee");
        binder.bindInstanceFields(this);

        setFields();
        setButtons();
    }

    public void setEmployee(Employee employee) {
        this.employee = employee;
        binder.setBean(this.employee);
    }

    private void setFields() {
        firstName.setClearButtonVisible(true);
        secondName.setClearButtonVisible(true);
        lastName.setClearButtonVisible(true);
        birthday.setValue(LocalDate.now());
        department.setItems(departmentList);
        department.setItemLabelGenerator(Department::getName);

        layout.add(firstName, secondName,
                lastName, birthday, department);
        add(layout);
    }

    private void setButtons() {
        save.addThemeVariants(ButtonVariant.LUMO_PRIMARY);
        save.addClickShortcut(Key.ENTER);
        cancel.addClickShortcut(Key.ESCAPE);

        save.addClickListener(event -> validateAndSave());
        cancel.addClickListener(event -> fireEvent(new CloseEvent(this)));

        binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid()));

        getFooter().add(cancel, save);
    }

    private void validateAndSave() {
        try {
            binder.writeBean(employee);
            fireEvent(new SaveEvent(this, employee));
        } catch (ValidationException e) {
            throw new RuntimeException(e);
        }
    }

    @Getter
    public static abstract class EditEmployeeEvent
            extends ComponentEvent<EditEmployeeModalBox> {
        private final Employee employee;

        public EditEmployeeEvent(EditEmployeeModalBox source, Employee employee) {
            super(source, false);
            this.employee = employee;
        }
    }

    public static class SaveEvent extends EditEmployeeEvent {
        public SaveEvent(EditEmployeeModalBox source, Employee employee) {
            super(source, employee);
        }
    }

    public static class CloseEvent extends EditEmployeeEvent {
        public CloseEvent(EditEmployeeModalBox source) {
            super(source, null);
        }
    }

    public <T extends ComponentEvent<?>> Registration addListener(Class<T> eventType,
                                                                  ComponentEventListener<T> listener) {
        return getEventBus().addListener(eventType, listener);
    }
}
